package handlers

import (
	"context"
	"crypto/sha256"
	"errors"
	"fmt"
	"gitee.com/aqchain/backend/config"
	"gitee.com/aqchain/backend/models"
	"gitee.com/aqchain/backend/utils"
	"gitee.com/aqchain/go-ethereum/common"
	"github.com/gin-gonic/gin"
	"io"
	"mime/multipart"
	"os"
	"path/filepath"
	"strconv"
)

func (h *Handler) AddItem(c *gin.Context) {
	err := h.addItem(c)
	if err != nil {
		c.JSON(200, gin.H{
			"success": false,
			"message": err.Error(),
		})
		return
	}
	c.JSON(200, gin.H{
		"success": true,
	})
}

func (h *Handler) addItem(c *gin.Context) error {

	// 获取并检查参数
	userID, err := strconv.ParseUint(c.PostForm("userID"), 10, 64)
	category, err := strconv.ParseUint(c.PostForm("category"), 10, 64)
	file, err := c.FormFile("file")
	if err != nil {
		return errParams
	}
	name := c.PostForm("name")
	description := c.PostForm("description")
	fileType := c.PostForm("fileType")

	if fileType == "" {
		return errFileType
	}

	// 检查用户
	var user models.User
	err = h.db.First(&user, models.User{ID: uint(userID)}).Error
	if err != nil {
		return errUserNotFound
	}

	// 生成文件的哈希值
	fileHash, err := getFileHash(file)
	if err != nil {
		return err
	}

	// 检查文件是否已经存在
	var existedFile models.Item
	h.db.Where(&models.Item{FileHash: fileHash}).First(&existedFile)
	if existedFile.ID > 0 {
		return errFileExist
	}

	// 文件上传保存
	wd, _ := os.Getwd()
	path := filepath.Join(wd, h.config.TempDir, config.ItemDir, fileHash)
	err = c.SaveUploadedFile(file, path)
	if err != nil {
		return err
	}

	// 调用合约
	// todo 处理没有绑定区块链地址的
	if user.Address == "" {
		return errors.New("未绑定区块链地址")
	}
	ownerAddress, _ := h.client.GetOwner(fileHash)
	if ownerAddress.Hex() != "0x0000000000000000000000000000000000000000" {
		return errors.New("区块链已有记录")
	}
	txHash, err := h.client.SetOwner(context.Background(), fileHash, common.HexToAddress(user.Address))
	if err != nil {
		return err
	}

	// 保存上传结果数据
	trade := models.Trade{
		FromUserID: user.ID,
		ToUserID:   user.ID,
		TxHash:     txHash,
	}
	item := models.Item{
		Name:        name,
		Description: description,
		Category:    uint8(category),
		TxHash:      txHash,
		FileHash:    fileHash,
		FileName:    file.Filename,
		FileSize:    file.Size,
		FileType:    fileType,
		CreatorID:   user.ID,
		OwnerID:     user.ID,
		Trades:      []models.Trade{trade},
	}

	err = h.db.Create(&item).Error

	if err != nil {
		return err
	}

	return nil
}

func (h *Handler) SearchItemList(c *gin.Context) {
	var items []models.Item

	// 获取参数并检查
	current, err := strconv.Atoi(c.Query("current"))
	pageSize, err := strconv.Atoi(c.Query("pageSize"))

	creatorID, _ := strconv.Atoi(c.Query("creatorID"))
	ownerID, _ := strconv.Atoi(c.Query("ownerID"))
	onSale, _ := strconv.ParseBool(c.Query("onSale"))

	name := c.Query("name")
	category, _ := strconv.Atoi(c.Query("category"))
	status, _ := strconv.Atoi(c.Query("status"))

	if err != nil {
		c.JSON(200, gin.H{
			"success":  false,
			"data":     items,
			"current":  0,
			"pageSize": 20,
			"message":  fmt.Sprintf("params err %s", err.Error()),
		})
		return
	}

	// 条件查询
	tx := h.db.Preload("Creator").Preload("Owner").
		Limit(pageSize).Offset((current - 1) * pageSize).
		Where(&models.Item{CreatorID: uint(creatorID), OwnerID: uint(ownerID), Name: name, Category: uint8(category), Status: uint8(status)})

	if onSale {
		//tx = tx.Where(&models.Item{Status: 1})
		tx = tx.Where("status = 2 OR status = 3 OR status = 4")
	}

	err = tx.Find(&items).Error

	if err != nil {
		c.JSON(200, gin.H{
			"success":  false,
			"data":     items,
			"current":  current,
			"pageSize": pageSize,
			"message":  fmt.Sprintf("params err %s", err.Error()),
		})
		return
	}

	c.JSON(200, gin.H{
		"success": true,
		"data":    items,
		"page":    1,
		"total":   len(items),
		//"pageSize": pageSize,
	})
}

func (h *Handler) GetFile(c *gin.Context) {
	fileHash := c.Query("fileHash")
	wd, _ := os.Getwd()
	c.File(filepath.Join(wd, h.config.TempDir, config.ItemDir, fileHash))
}

func (h *Handler) GetItem(c *gin.Context) {
	itemID, err := strconv.Atoi(c.Query("itemID"))
	if err != nil {
		c.JSON(200, gin.H{
			"success": false,
			"message": fmt.Sprintf("params err %s", err.Error()),
		})
		return
	}
	item := models.Item{ID: uint(itemID)}
	err = h.db.Preload("Owner").Preload("Creator").
		Preload("Trades").Preload("Trades.From").Preload("Trades.To").
		First(&item).
		Error
	if err != nil {
		c.JSON(200, gin.H{
			"success": false,
			"message": fmt.Sprintf("db err %s", err.Error()),
		})
		return
	}

	c.JSON(200, gin.H{
		"success": true,
		"data":    item,
	})
}

func (h *Handler) GetCertificate(c *gin.Context) {
	fileHash := c.Query("fileHash")
	wd, _ := os.Getwd()
	c.File(filepath.Join(wd, h.config.TempDir, config.CertificationDir, fileHash) + ".png")
}

func getFileHash(file *multipart.FileHeader) (string, error) {
	var fileHash string
	src, err := file.Open()
	if err != nil {
		return fileHash, err
	}
	hash := sha256.New()
	_, err = io.Copy(hash, src)
	if err != nil {
		return fileHash, err
	}
	fileHash = utils.Encode(hash.Sum(nil))
	return fileHash, nil
}
